package com.petrochina.cloudconfig.biz;


import com.alibaba.fastjson.JSON;
import com.petrochina.cloudconfig.domain.Server;
import com.petrochina.cloudconfig.domain.env.EnvConfig;
import com.petrochina.cloudconfig.domain.DeployServer;
import com.petrochina.cloudconfig.domain.Env;
import com.petrochina.cloudconfig.domain.env.EnvSetting;
import com.petrochina.cloudconfig.mapper.ServerMapper;
import com.petrochina.cloudconfig.util.*;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.apache.tools.zip.ZipOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.*;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.util.*;

/**
 * 功能业务
 */
@Service
public class FuncBiz {

    private static Logger logger = LoggerFactory.getLogger(FuncBiz.class);


    @Value("${my.env.path}")
    private String envRootPath;


    @Value("${my.pkg.path}")
    private String pkgRootPath;

    @Value("${my.cache.path}")
    private String cacheRootPath;

    @Value("${my.server.path}")
    private String serverRootPath;


    @Autowired
    private ServerMapper serverMapper;


    @Autowired
    private RedisTemplate redisTemplate;




    public void pack(String pkgFullName, String envName) {

        //step1 env ready
        Env env = getEnv(serverRootPath,pkgFullName,envName);
        //step2 change zip file values and copy
        String newFilePath = getNewZipFilePath(env,pkgRootPath,cacheRootPath,pkgFullName,envName);
        //是否存在文件,存在则发布
        if(newFilePath!=null){
            //发布到服务器(如果需要的话）
            deploy(newFilePath,env);
        }



        
    }

    private String getNewZipFilePath(Env env, String pkgRootPath, String cacheRootPath, String pkgFullName,String envName) {
        //getDev pkg file
        File pkgFile = PkgUtil.getPkgFile(pkgRootPath, pkgFullName);
        //getDev cache path
        String absolutePath = pkgFile.getParentFile().getAbsolutePath();
        String cachePath = cacheRootPath + absolutePath.substring(pkgRootPath.length()) + File.separator + envName;
        //getDev new file path
        String newFilePath = absolutePath+File.separator+envName+File.separator+pkgFile.getName();
        //准备配置
        List<EnvConfig> envConfigs = env.getEnvConfigs();
        //getDev zip file
        ZipFile zipFile = null;
        try {
            zipFile = new ZipFile(pkgFile);
            if (envConfigs != null && envConfigs.size() > 0) {
                //处理配置
                prepareConfigs(zipFile, envConfigs);
                //解压需要修改的文件到缓存并进行修改
                unZipAndModify(zipFile, envConfigs, cachePath);
                //传输文件到新文件中
                ZipAndCopy(zipFile, newFilePath, envConfigs, cachePath);
                //清理缓存目录
                clearCache(cachePath);

            }
            zipFile.close();
        } catch (Exception e) {
            if(zipFile!=null){
                try {
                    zipFile.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            e.printStackTrace();
            return null;
        }
        return newFilePath;
    }

    private Env getEnv(String serverRootPath,String pkgFullName, String envName) {
        Env env = EnvUtil.getEnv(pkgFullName, envName);

        EnvSetting envSetting = EnvSettingUtil.getEnvSetting(serverRootPath, pkgFullName, envName);
        //get ips
        List<String> ips = EnvSettingUtil.getIPs(envSetting);
        Map<String,DeployServer> deployServerMap = null;
        if(ips.size()>0){
            deployServerMap = getDeployServerMap(ips);
        }
        EnvUtil.setEnv(env,envSetting,deployServerMap);

        return env;
    }


    /**
     * 准备配置
     *
     * @param zipFile
     * @param envConfigs
     */
    public void prepareConfigs(ZipFile zipFile, List<EnvConfig> envConfigs) {

        Map<String, EnvConfig> needSearchConfMap = new HashMap<>();
        for (int i = 0; i < envConfigs.size(); i++) {
            EnvConfig tempEnvConfig = envConfigs.get(i);
            if (tempEnvConfig.getFilePath() == null) {
                needSearchConfMap.put(tempEnvConfig.getFileName(), tempEnvConfig);
            }
        }
        //if need search
        if (needSearchConfMap.size() > 0) {
            Enumeration<ZipEntry> allEntries = zipFile.getEntries();
            while (allEntries.hasMoreElements()) {
                ZipEntry tempZipEntry = allEntries.nextElement();
                String tempZipEntryName = tempZipEntry.getName();
                String tempEntryFileName = tempZipEntryName.substring(tempZipEntryName.lastIndexOf("/") + 1);
                EnvConfig envConfig = needSearchConfMap.get(tempEntryFileName);
                if (envConfig != null) {
                    envConfig.setFilePath(tempZipEntryName);
                }

            }
        }


    }

    /**
     * 解压并修改
     * @param zipFile
     * @param envConfigs
     * @param cachePath
     */
    public void unZipAndModify(ZipFile zipFile, List<EnvConfig> envConfigs, String cachePath) throws IOException {
        for (int i = 0; i < envConfigs.size(); i++) {

            EnvConfig envConfig = envConfigs.get(i);
            String filePath = envConfig.getFilePath();
            ZipEntry entry = zipFile.getEntry(filePath);
            if(entry!=null){
                //unzip
                BufferedInputStream bis = new BufferedInputStream(zipFile.getInputStream(entry));
                File cacheFile = new File(cachePath + File.separator + filePath);
                if(!cacheFile.exists()){
                    cacheFile.getParentFile().mkdirs();
                }
                FileOutputStream fos = new FileOutputStream(cacheFile);
                byte[] buf = new byte[1024];
                int len = 0 ;
                while((len=bis.read(buf,0,1024))!=-1){
                    fos.write(buf,0,len);
                }
                fos.flush();
                fos.close();
                bis.close();
                //modify
                PropertiesUtil.writeProperties(cacheFile.getAbsolutePath(), envConfig.getConfigMap());
            }

        }
    }

    public void ZipAndCopy(ZipFile zipFile, String newFilePath, List<EnvConfig> envConfigs, String cachePath) throws Exception {
        File file = new File(newFilePath);
        if(!file.exists()){
            file.getParentFile().mkdirs();
        }
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(newFilePath));
        Enumeration<ZipEntry> entries = zipFile.getEntries();
        while(entries.hasMoreElements()){
            ZipEntry zipEntry = entries.nextElement();
            boolean needCopy = true;
            for (int i = 0; i < envConfigs.size(); i++) {
                if(zipEntry.getName().equals(envConfigs.get(i).getFilePath())){
                    needCopy = false;
                    break;
                }

            }
            if(needCopy){
                //copy
                zos.putNextEntry(zipEntry);
                if(!zipEntry.isDirectory()){
                    copyStream(zipFile.getInputStream(zipEntry),zos);
                }
                zos.closeEntry();
            }

        }
        zos.setEncoding("gbk");
        File cachePathFile = new File(cachePath);
        if(cachePathFile.exists()){
            ZipUtil.zip(zos,cachePathFile,"");
        }
        //over
        zos.close();
    }

    /**
     * inputstream  to  zipoutputstream
     * @param input
     * @param output
     * @throws IOException
     */
    private void copyStream(InputStream input, ZipOutputStream output) throws IOException {
        int bytesRead;
        byte[] bytes = new byte[1024];
        while((bytesRead = input.read(bytes))!=-1){
            output.write(bytes,0,bytesRead);
        }
    }

    /**
     * clean cache file
     * @param cachePath
     */
    public void clearCache(String cachePath) {
        FileUtil.deleteFile(cachePath);

    }

    /**
     * 发布到服务器上
     * @param newFilePath 本地文件路径
     * @param env 环境对象
     */
    public void deploy(String newFilePath, Env env) {
        //准备服务器发布
        List<DeployServer> servers = env.getDeployServers();
        File file = new File(newFilePath);
        if(servers!=null && servers.size()>0){
            for (int i = 0; i < servers.size(); i++) {
                DeployServer ds = servers.get(i);

                String deployPath = ds.getDeployPath()+File.separator+file.getName();
                SSH2Util.copyFileToServer(ds.getServerIp(),ds.getUsername(),ds.getPassword(),newFilePath,deployPath);
                logger.info(ds.getServerIp()+"发布成功");
            }

        }

    }

    public Map<String,DeployServer> getDeployServerMap(List<String> ips) {
        Map<String,DeployServer> deployServerMap = new HashMap<>();
        List<DeployServer> deployServers = serverMapper.list(ips);
        for (int i = 0; i < deployServers.size(); i++) {
            DeployServer deployServer = deployServers.get(i);
            //set config
            ServerUtil.setServerInfo(deployServer,serverRootPath);
            //add to map
            deployServerMap.put(deployServer.getServerIp(),deployServer);
        }
        return deployServerMap;
    }


}
